/*
    Speedbump aka Project Kimbula
    Copyright (C) 2010  Ping Shin Ching

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see http://www.gnu.org/licenses.

	Ping Shin Ching
	righteous.ninja@gmail.com OR ping_sc@hotmail.com
	More information at http://www.tidytutorials.com/p/speedbump.html
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "headerhandler.h"
#include "helpers.h"
#include "application.h"


/*************************************************************************
 * Decode headers
 *************************************************************************/
int HeaderHandler::DecodeHeaders(const char* text, int buflen){

	if(text==0 || buflen==0)
		return 0;

	char* end, *colon;
	char* start = (char*)text;
	int tempint;

	if((end=strnstrn(start, buflen, "\r\n", strlen("\r\n")))!=0){

		noofheaders	= 0;
		headers		= 0;
		requestline	= 0;

		if(end-start==0){
			return 0;
		}

		if((requestline = (char*)malloc(sizeof(char)*((end-start)+1)))==0){
			return 0;
		}
		memset(requestline, 0, (end-start)+1);
		strncpy(requestline, start, (end-start));

		start=end+1;

		while(	(colon=strnstrn(start, buflen-(start-text), ":", 1))!=0 && 
				(end=strnstrn(colon, buflen-(colon-text), "\r\n", 2))!=0 ){
		
			headers = (Header*)realloc(headers, (noofheaders+1)*sizeof(Header));
		
			if(!headers){
				return 0;
			}
			start++;

			if(colon-start<=0){
				continue;
			}

			memset(headers[noofheaders].header,0, HEADER_HEADER_SIZE);
			
			/*	HEADER_HEADER_SIZE-1 because we are going to leave 1 char for the terminating null char.
				Without the '\0' strlen will fail when sizing
			*/
			strncpy(headers[noofheaders].header, start, HEADER_HEADER_SIZE-1<(colon-start)?HEADER_HEADER_SIZE-1:(colon-start));
					
			tempint=end-(colon+2);

			if(tempint > 0){
				headers[noofheaders].value = (char*)malloc(sizeof(char)*(tempint+1));
				memset(headers[noofheaders].value, 0, tempint+1);
				strncpy(headers[noofheaders].value, colon+2, tempint);
			}
			else{
				headers[noofheaders].value = (char*)malloc(sizeof(char));
				memset(headers[noofheaders].value, 0, 1);

			}
			
			start=end+1;
			noofheaders++;

		
		}

		return 1;
	}
	else
		return 0;
}


/*************************************************************************
 * Constructor
 *************************************************************************/
HeaderHandler::HeaderHandler(){
	noofheaders	= 0;
	headers		= 0;
	requestline	= 0;
}


/*************************************************************************
 * Destructor 
 *************************************************************************/
HeaderHandler::~HeaderHandler(){
	FreeHeaders();
}


/*************************************************************************
 * Display the headers
 *************************************************************************/
void HeaderHandler::DisplayHeaders(){
	
	int i;
	printf("--------------------Headers-----------------------\n%s\n", requestline?requestline:"no-request-line");
	for(i=0;i<noofheaders;i++){
		if( strcmp(headers[i].header, HEADER_NULL_HEADER)!=0)
			printf(">%s:%s\n", headers[i].header, headers[i].value?headers[i].value:"");
	}
}


/*************************************************************************
 * Free the headers
 *************************************************************************/
void HeaderHandler::FreeHeaders(){
	int i;
	if(requestline){
		free(requestline);
		requestline=0;
	}
	if(headers){
		for(i=0;i< noofheaders;i++){
			free(headers[i].value);
		}
		free(headers);
		headers=0;
	}
}


/*************************************************************************
 * Find the header
 *************************************************************************/
Header* HeaderHandler::findHeader( const char* header){
	int i;
	for(i=0;i< noofheaders;i++){
		if(strcasecmp(headers[i].header, header)==0){

			return &headers[i];
		}
	}
	return 0;
}


/*************************************************************************
 * Get the size of the serialized header
 *************************************************************************/
unsigned int HeaderHandler::SerializedHeaderSize(){
	unsigned int totalsize;
	int i;

	if(requestline)
		totalsize = strlen(requestline)+strlen("\r\n");
	else
		return 0;
	if(headers){
		for(i=0; i < noofheaders ;i++){
			if(strcmp(headers[i].header,HEADER_NULL_HEADER)==0)
				continue;
			totalsize+=(strlen(headers[i].header)+strlen(": ")+strlen(headers[i].value)+strlen("\r\n"));
		}
	}
	//totalsize++;//'\0'
	return totalsize;
}


/*************************************************************************
 * Serialize the headers
 *************************************************************************/
int HeaderHandler::SerializeHeaders( char* buffer, unsigned int* buflen){
	int i;
	if(*buflen<SerializedHeaderSize()+1 /*\0*/){
		loge<<"Buffer length is not enough to serialize headers"<<lend;
		return 0;
	}
	sprintf(buffer, "%s\r\n", requestline); 
	for(i=0;i<noofheaders;i++){
		if(strcmp(headers[i].header,HEADER_NULL_HEADER)==0)
			continue;
		sprintf(buffer+strlen(buffer), "%s: %s\r\n", headers[i].header, headers[i].value);
	}
	return i;
}

/*************************************************************************
 * Replace a header
 *************************************************************************/
int HeaderHandler::ReplaceHeader( const char* header, const char* value){
	
	Header* h = findHeader(header);
	if(!h){
		if(!(headers = (Header*)realloc(headers, (noofheaders+1)*sizeof(Header))))
			return 0;
		
		h=&(headers[noofheaders]);
		h->value=0;
		noofheaders++;
	}

	memset(h->header, 0, HEADER_HEADER_SIZE);
	strncpy(h->header, header, HEADER_HEADER_SIZE-1<strlen(header)?HEADER_HEADER_SIZE-1:strlen(header));
	h->value = (char*)realloc( h->value, sizeof(char)*(strlen(value)+1));
	memset(h->value, 0, strlen(value)+1);
	strncpy(h->value, value, strlen(value));

	return 1;
}

/*************************************************************************
 * Add a header
 *************************************************************************/
int HeaderHandler::AddHeader( const char* header, const char* value){
	return ReplaceHeader(header, value);	 //for now
}


/*************************************************************************
 * Remove a header
 *************************************************************************/
int HeaderHandler::RemoveHeader( const char* header){
	Header* h = findHeader(header);
	if(h){
		strcpy(h->header,HEADER_NULL_HEADER);
	}
	return 1;
}


/*************************************************************************
 * Get the request line
 *************************************************************************/
const char* HeaderHandler::GetRequestLine() const {
	return requestline;
}


/*************************************************************************
 * Set the request line
 *************************************************************************/
int HeaderHandler::SetRequestLine(const char* reql){
	char * tmp = (char*)realloc(requestline, strlen(reql)+1);
	if(tmp!=0){
		requestline = tmp;
		memset(requestline,0, strlen(reql)+1);
		memcpy(requestline, reql, strlen(reql));
		return 1;
	}
	else
		return 0;
}


/*************************************************************************
 * Get a header
 *************************************************************************/
const char* HeaderHandler::GetHeader(const char* header){
	Header* h = findHeader(header);
	if(h){
		return h->value;
	}
	else{
		return 0;
	}
}


/*************************************************************************
 * Cut the received headers removing the hostname/domain name etc
 *************************************************************************/
void HeaderHandler::RecutRequestLine(){
	char * start, *end;
	if(requestline && ! IsConnect()){
		if((start=strstr(requestline, "http://"))|| (start=strstr(requestline, "https://"))){
			end=start+strlen("https://");
			if((end=strstr(end,"/"))  ){
				memmove(start, end, strlen(requestline)+1-(end-requestline));
				requestline=(char*)realloc(requestline, strlen(requestline)+1);

			}
		}
	}
}


/*************************************************************************
 * Check if the request is a CONNECT request
 *************************************************************************/
int HeaderHandler::IsConnect(){
	if(stristr(requestline,HEADER_REQLINE_CONNECT)-requestline==0 || 
		stristr(requestline, HEADER_REQLINE_CONNECTOK)!=0){
		return 1;
	}

	return 0;
}
